package org.nobject.common.bean;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.nobject.common.exception.AmbiguousException;
import org.nobject.common.exception.ConvertException;
import org.nobject.common.exception.InvokeException;
import org.nobject.common.exception.NoFoundException;
import org.nobject.common.exception.UnSupportException;
import org.nobject.common.lang.ClassUtils;
import org.nobject.common.lang.ObjectUtils;
import org.nobject.common.lang.StringUtils;
import org.nobject.common.text.WordUtils;

/**
 * Bean工具类<br>
 * 
 * 不同于ClassUtils,property的获取及设置操作都经对应的set和get方法来实现<br>
 * 默认约定:可识别的property应对应有public修饰,set和get为前缀,与property第一个字母大写组成命名的方法,set的参数类型及get的返回类型需与property类型一致。
 * 
 * @author bianrongjun
 * @version 1.0
 */
public class BeanUtils {
	
	/******************************************************************************************************************
	 * 
	 *  Property 区域
	 * 
	 *******************************************************************************************************************/
	
	/** 
	 * 设置属性值
	 * @param bean 对象
	 * @param propName 属性名称
	 * @param propValue 属性值
	 * 
	 * @throws UnSupportException 当自动值转换遇到类型不正确时发生异常 
	 * @throws InvokeException Method.invoke时引起的异常
	 * @throws NoFoundException 未找到Set方法时抛出的异常
	 * @throws AmbiguousException 方法个数过多时引起异常
	 * 
	 * @see #setProperty(Object, Method, Object)
	 */
	public static void setProperty(Object bean, String propName, Object propValue) throws UnSupportException,InvokeException, NoFoundException, AmbiguousException{
		Method setMethod = getSetMethod(bean.getClass(),propName);
		if(setMethod==null) throw new NoFoundException("未找到"+bean.getClass().getName()+"#"+propName+"的set方法");
		setProperty(bean, setMethod, propValue);
	}
	
	/** 
	 * 设置属性值
	 * @param bean 对象
	 * @param setMethod 属性set方法
	 * @param propValue 属性值
	 * 
	 * @throws UnSupportException 当自动值转换遇到类型不正确时发生异常
	 * @throws InvokeException Method.invoke时引起的异常
	 */
	public static void setProperty(Object bean, Method setMethod, Object propValue) throws UnSupportException,InvokeException {
		try {
			Object o=ObjectUtils.toObject(propValue, setMethod.getParameterTypes()[0]);
			setMethod.invoke(bean,o);
		} catch (Exception e) {
			throw new InvokeException(e);
		}
	}
	
	/** 
	 * 获取属性值
	 * @param bean 对象
	 * @param propName 属性名称
	 * 
	 * @throws InvokeException Method.Invoke时引起的异常
	 * @throws NoFoundException 未找方法时发生的异常
	 * @throws AmbiguousException 方法个数过多时引起异常
	 */
	public static Object getProperty(Object bean, String propName) {
		Method m = getGetMethod(bean.getClass(), propName);
		if(m==null) throw new RuntimeException("没有发现"+bean.getClass().getName()+"#"+propName+"对应的get方法");
		try {
			return m.invoke(bean);
		} catch (Exception e) {	
			throw new RuntimeException(e);
		}
	}
	
	/******************************************************************************************************************
	 * 
	 *  Method 区域
	 * 
	 *******************************************************************************************************************/
	
	/**
	 * 获取属性SET方法<br>
	 * 匹配以set开头+属性名首字母大写的方法
	 * @param clazz 类
	 * @param propName 属性名
	 * 
	 * @throws AmbiguousException 方法个数过多时引起异常
	 */
	public static Method getSetMethod(Class clazz, String propName) throws AmbiguousException {
		return getSetMethod(clazz, propName,null);
	}
	
	/**
	 * 获取属性SET方法<br>
	 * 匹配以set开头+属性名首字母大写,参数为指定属性类型的方法
	 * @param clazz 类
	 * @param propName 属性名
	 * @param propType 属性类型
	 * 
	 * @throws AmbiguousException 方法个数过多时引起异常
	 */
	public static Method getSetMethod(Class clazz, String propName,Class propType) throws AmbiguousException {
		String methodName="set" + WordUtils.firstToUpperCase(propName);
		Class[] params=propType==null?null:new Class[]{propType};
		Method[] methods=ClassUtils.getMethods(clazz,methodName,params,1,null,new int[]{Modifier.PUBLIC});
		if(methods==null||methods.length==0) return null;
		if(methods.length>1) throw new AmbiguousException("无法识别多个"+clazz.getName()+"#"+methodName+"方法");
		return methods[0];
	}
	
	/** 
	 * 获取所有的SET方法
	 * @param clazz
	 */
	public static Method[] getSetMethods(Class clazz) {
		return ClassUtils.getMethods(clazz, "set*", null, 1,null,new int[]{Modifier.PUBLIC});
	}
	
	/**
	 * 获取属性GET方法<br>
	 * 匹配以get开头+属性名首字母大写、返回类型为属性类型 的方法,属性类型为null时返回类型不辨别
	 * @param clazz 类
	 * @param propName 属性名
	 * 
	 * @throws AmbiguousException 方法个数过多时引起异常
	 */
	public static Method getGetMethod(Class clazz,String propName){
		return getGetMethod(clazz, propName, null);
	}
	
	/**
	 * 获取属性GET方法<br>
	 * 匹配以get开头+属性名首字母大写、返回类型为属性类型 的方法,属性类型为null时返回类型不辨别
	 * @param clazz 类
	 * @param propName 属性名
	 * @param propType 属性类型
	 * 
	 * @throws AmbiguousException 方法个数过多时引起异常
	 */
	public static Method getGetMethod(Class clazz,String propName,Class propType){
		String[] methodNames=new String[]{"get"+WordUtils.firstToUpperCase(propName)};
		Method[] methods=ClassUtils.getMethods0(clazz,methodNames,null,0,propType,new int[]{Modifier.PUBLIC});
		if(methods==null||methods.length==0) return null;
		if(methods.length>1) throw new RuntimeException("无法识别多个"+clazz.getName()+"#"+StringUtils.toString(methodNames)+"方法");
		return methods[0];
	}
	
	/** 
	 * 获取所有的SET方法
	 * @param clazz
	 */
	public static Method[] getGetMethods(Class clazz) {
		return ClassUtils.getMethods(clazz, "get*", null, 1,null,new int[]{Modifier.PUBLIC});
	}
	
	/**
	 * 获取属性类型
	 * @param beanClass bean类型
	 * @param propName 属性名
	 * 
	 * @throws NoFoundException 没有找到属性时发生的异常
	 */
	public static Class getPropertyClass(Class beanClass, String propName) throws NoFoundException {
		return ClassUtils.getFieldClass(beanClass, propName);
	}
	
	/** 
	 * 根据类new个实例
	 * @param beanClass
	 * @param propsMap
	 */
	public static Object setBeanByClass(Class beanClass,Map propsMap) throws UnSupportException{
		try {
			return setBean(beanClass.newInstance(), propsMap);
		} catch (Exception e) {
			throw new UnSupportException(e);
		}
	}
	
	/** 
	 * 批量转换Map到 Class的实例
	 * @param beanClass
	 * @param listPropMap
	 * @return
	 * @throws UnSupportException
	 */
	public static List setBeansByClass(Class beanClass,List listPropMap) throws UnSupportException{
		for(int i=0;i<listPropMap.size();i++){
			listPropMap.set(i, setBeanByClass(beanClass,(Map)listPropMap.get(i)));
		}
		return listPropMap;
	}
	
	/** 
	 * 取出Map键值对,找到键值在Bean实例对应的set方法进行注入<br>
	 * 会忽略不存在的键值及未存在的set方法
	 * @param bean 对象实例
	 * @param propsMap 属性Map
	 * 
	 * @throws UnSupportException 当自动值转换遇到类型不正确时发生异常
	 * @throws InvokeException Method.invoke时引起的异常
	 */
	public static Object setBean(Object bean, Map propsMap) throws UnSupportException,InvokeException{
		if (propsMap == null) return bean;
		if (Map.class.equals(bean.getClass()) || Map.class.isAssignableFrom(bean.getClass())) {
			Map mbean = (Map) bean;
			mbean.putAll(propsMap);
		} else {
			for (Iterator iterator = propsMap.keySet().iterator(); iterator.hasNext();) {
				String propName = (String) iterator.next();
				Object propValue= propsMap.get(propName);
				Method setMethod;
				try {
					setMethod = getSetMethod(bean.getClass(), propName);
				} catch (AmbiguousException e) {
					continue;
				}
				if(setMethod==null) continue;
				try {
					propValue=ObjectUtils.toObject(propValue, setMethod.getParameterTypes()[0]);
				} catch (ConvertException e) {
					throw new UnSupportException("无法进行方法的值转换:"+bean.getClass().getName()+"#"+setMethod.getName()+"<-->"+propValue+":"+e.getMessage());
				}
				setProperty(bean,setMethod,propValue);
			}
		}
		return bean;
	}
	
	
	
	/**
	 * bean转换成Map<br>
	 * 忽略方法无法识别的异常和执行时的异常
	 * @param bean 实例
	 */
	public static Map bean2Map(Object bean) {
		if (bean == null) return null;
		Map map = new LinkedHashMap();
		Class c=bean.getClass();
		Field[] fs = ClassUtils.getFields(c);
		for (Field field:fs) {
			Method m=getGetMethod(c, field.getName());
			if(m==null) continue;
			Object o=null;
			try {
				o= m.invoke(bean);
			} catch (Exception e) {	
				continue;
			}
			map.put(field.getName(), o);
		}
		return map;
	}
	
	/** 
	 * bean数组转换成List<Map>
	 * @param beans 实例s
	 */
	public static List<Map> beans2Maps(Object[] beans){
		List l=new LinkedList();
		for(Object o:beans){
			l.add(bean2Map(o));
		}
		return l;
	}
	
	/** 
	 * 克隆
	 * @param bean
	 * @return
	 */
	public static Object clone(Object bean){
		try {
			Object nb=bean.getClass().getConstructors()[0].newInstance();
			setBean(nb, bean2Map(bean));
			return nb;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	/** 
	 * 比较两个对象的属性
	 * @param o1 对象1
	 * @param o2 对象2
	 * @param property 属性
	 */
	public static boolean compareProperty(Object o1,Object o2,String property) throws NoFoundException,AmbiguousException,InvokeException{
		return compareProperty(o1, o2, property,property);
	}
	
	/** 
	 * 比较两个对象的属性
	 * @param o1 对象1
	 * @param o2 对象2
	 * @param propName1 属性1
	 * @param propName2 属性2
	 */
	public static boolean compareProperty(Object o1,Object o2,String propName1,String propName2) throws NoFoundException,AmbiguousException,InvokeException{
		Object v1=getProperty(o1, propName1);
		Object v2=getProperty(o2, propName2);
		if(v1==null&&v2!=null) return false;
		if(v1!=null&&v2==null) return false;
		if(!v1.equals(v2)) return false;
		return true;
	}
	
//	/**
//	 * 克隆
//	 * @param bean 对象
//	 */
//	public static Object byteClone(Object bean) {
//		try {
//			ByteArrayOutputStream baos = new ByteArrayOutputStream();
//			ObjectOutputStream out = new ObjectOutputStream(baos);
//			out.writeObject(bean);
//			out.close();
//			ByteArrayInputStream bin = new ByteArrayInputStream(baos.toByteArray());
//			ObjectInputStream in = new ObjectInputStream(bin);
//			Object no = in.readObject();
//			in.close();
//			return (no);
//		} catch (ClassNotFoundException e) {
//			throw new InternalError(e.toString());
//		} catch (StreamCorruptedException e) {
//			throw new InternalError(e.toString());
//		} catch (IOException e) {
//			throw new InternalError(e.toString());
//		}
//	}
}
